From 7d67a7ab23ff55d302bc10af2f9ee758668f7f2a Mon Sep 17 00:00:00 2001 From: "kaf24@firebug.cl.cam.ac.uk" Date: Fri, 21 Apr 2006 10:11:00 +0100 Subject: [PATCH] Fix the "hda lost interrupt" issue when creating a VMX guest on a PAE host. Occasionally when injecting an IDE DMA interrupt into the guest, a page fault occurs (e.g., because the IDT mapping is not present in shadow pagetables). This causes an immediate vmexit and, because it occurred during event delivery, the original VM_ENTRY_INTR_INFO_FIELD is kept in IDT_VECTORING_INFO_FIELD. The current code copies IDT_VECTORING_INFO_FIELD back to VM_ENTRY_INTR_INFO_FIELD, intending that the interrupt will be injected again on next vmresume. However, there is a corner case: if, before the next vmresume, a timer interrupt happened then vmx_intr_assist may overwrite the information on VM_ENTRY_INTR_INFO_FIELD, and the IDE DMA interrupt is effectively lost. This patch checks the IDT_VECTORING_INFO_FIELD in vmx_intr_assist and, if it is set, copies it to VM_ENTRY_INTR_INFO_FIELD and returns. Signed-off-by: Yunhong Jiang Signed-off-by: Eddie Dong --- xen/arch/x86/hvm/vmx/io.c | 26 +++++++++++++++++++++++++- xen/arch/x86/hvm/vmx/vmx.c | 19 +------------------ 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/xen/arch/x86/hvm/vmx/io.c b/xen/arch/x86/hvm/vmx/io.c index 5be8bed971..9835445d61 100644 --- a/xen/arch/x86/hvm/vmx/io.c +++ b/xen/arch/x86/hvm/vmx/io.c @@ -153,6 +153,9 @@ asmlinkage void vmx_intr_assist(void) struct hvm_domain *plat=&v->domain->arch.hvm_domain; struct hvm_time_info *time_info = &plat->vpit.time_info; struct hvm_virpic *pic= &plat->vpic; + unsigned int idtv_info_field; + unsigned long inst_len; + int has_ext_irq; if ( v->vcpu_id == 0 ) hvm_pic_assist(v); @@ -162,8 +165,29 @@ asmlinkage void vmx_intr_assist(void) pic_set_irq(pic, 0, 1); } - if ( !cpu_has_pending_irq(v) ) return; + has_ext_irq = cpu_has_pending_irq(v); + __vmread(IDT_VECTORING_INFO_FIELD, &idtv_info_field); + if (idtv_info_field & INTR_INFO_VALID_MASK) { + __vmwrite(VM_ENTRY_INTR_INFO_FIELD, idtv_info_field); + __vmread(VM_EXIT_INSTRUCTION_LEN, &inst_len); + if (inst_len >= 1 && inst_len <= 15) + __vmwrite(VM_ENTRY_INSTRUCTION_LEN, inst_len); + + if (idtv_info_field & 0x800) { /* valid error code */ + unsigned long error_code; + __vmread(IDT_VECTORING_ERROR_CODE, &error_code); + __vmwrite(VM_ENTRY_EXCEPTION_ERROR_CODE, error_code); + } + if ( has_ext_irq ) + enable_irq_window(v); + + HVM_DBG_LOG(DBG_LEVEL_1, "idtv_info_field=%x", idtv_info_field); + + return; + } + + if ( !has_ext_irq ) return; if ( is_interruptibility_state() ) { /* pre-cleared for emulated instruction */ enable_irq_window(v); HVM_DBG_LOG(DBG_LEVEL_1, "interruptibility"); diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c index 052c11ba2a..86e3d9c763 100644 --- a/xen/arch/x86/hvm/vmx/vmx.c +++ b/xen/arch/x86/hvm/vmx/vmx.c @@ -2046,7 +2046,7 @@ void restore_cpu_user_regs(struct cpu_user_regs *regs) asmlinkage void vmx_vmexit_handler(struct cpu_user_regs regs) { - unsigned int exit_reason, idtv_info_field; + unsigned int exit_reason; unsigned long exit_qualification, eip, inst_len = 0; struct vcpu *v = current; int error; @@ -2056,23 +2056,6 @@ asmlinkage void vmx_vmexit_handler(struct cpu_user_regs regs) perfc_incra(vmexits, exit_reason); - __vmread(IDT_VECTORING_INFO_FIELD, &idtv_info_field); - if (idtv_info_field & INTR_INFO_VALID_MASK) { - __vmwrite(VM_ENTRY_INTR_INFO_FIELD, idtv_info_field); - - __vmread(VM_EXIT_INSTRUCTION_LEN, &inst_len); - if (inst_len >= 1 && inst_len <= 15) - __vmwrite(VM_ENTRY_INSTRUCTION_LEN, inst_len); - - if (idtv_info_field & 0x800) { /* valid error code */ - unsigned long error_code; - __vmread(IDT_VECTORING_ERROR_CODE, &error_code); - __vmwrite(VM_ENTRY_EXCEPTION_ERROR_CODE, error_code); - } - - HVM_DBG_LOG(DBG_LEVEL_1, "idtv_info_field=%x", idtv_info_field); - } - /* don't bother H/W interrutps */ if (exit_reason != EXIT_REASON_EXTERNAL_INTERRUPT && exit_reason != EXIT_REASON_VMCALL && -- 2.30.2